package xgenerator.jdbc;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class DataSourceFactory {
	
	/**
	 * 是否为容器数据源
	 */
	private boolean isContainerDataSource = false;
	
	/**
	 * propertiesMap
	 */
	private Map<String, Properties> propertiesMap;
	
	/**
	 * defaultDataSourceName
	 */
	private String defaultDataSourceName;
	
	/**
	 * constructor
	 * @param properties
	 */
	public DataSourceFactory(Map<String, Properties> propertiesMap, String defaultDataSourceName) {
		this.propertiesMap = propertiesMap;
		this.defaultDataSourceName = defaultDataSourceName;
	}
	
	/**
	 * constructor
	 * @param properties
	 */
	public DataSourceFactory(Map<String, Properties> propertiesMap, String defaultDataSourceName, boolean isContainerDataSource) {
		this.propertiesMap = propertiesMap;
		this.defaultDataSourceName = defaultDataSourceName;
		this.isContainerDataSource = isContainerDataSource;
	}
	
	/**
	 * 获取默认数据源
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @return
	 */
	public DataSource getDefaultDataSource() {
		Properties properties = this.propertiesMap.get(this.defaultDataSourceName);
		if(this.isContainerDataSource) {
			return new JndiDataSourceProxy(properties);
		} else {
			return new SimpleDataSource(properties);
		}
	}
	
	/**
	 * getDataSource
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @return
	 */
	public DataSource getDataSource(String dataSourceName) {
		Properties properties = this.propertiesMap.get(dataSourceName);
		if(this.isContainerDataSource) {
			return new JndiDataSourceProxy(properties);
		} else {
			return new SimpleDataSource(properties);
		}
	}
	
	/**
	 * 获取容器jndi命名数据源
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param jndiName
	 * @return
	 */
	public DataSource lookupDataSource(String jndiName) {
		DataSource dataSource = null;
		
		Context initContext = null;
		Context envContext = null;
		try {
			/*
			Hashtable<String, String> environment = new Hashtable<String, String>();
			environment.put("java.naming.factory.initial", factoryInitial);
			environment.put("java.naming.provider.url", jndiProviderURL);
			Context ctx = new InitialContext(environment);
			DataSource ds = (DataSource) ctx.lookup(jndiDataSource);
			*/
			
			initContext = new InitialContext();
			envContext = (Context) initContext.lookup("java:comp/env");
			dataSource = (DataSource) envContext.lookup(jndiName);
		} catch (NamingException ne) {
			if (null == dataSource) {
				throw new RuntimeException("Cannot find JNDI DataSource: " + jndiName, ne);
			}
		}
		
		return dataSource;
	}
	
	/**
	 * DataSourceAdapter
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @version 1.0
	 * @see 
	 * @since 1.0
	 */
	public abstract class DataSourceAdapter implements DataSource {
		public PrintWriter getLogWriter() throws SQLException {
			return null;
		}

		public int getLoginTimeout() throws SQLException {
			return 0;
		}

		public void setLogWriter(PrintWriter out) throws SQLException {
			
		}

		public void setLoginTimeout(int seconds) throws SQLException {
			
		}

		public boolean isWrapperFor(Class<?> iface) throws SQLException {
			return false;
		}

		public <T> T unwrap(Class<T> iface) throws SQLException {
			return null;
		}
	}
	
	/**
	 * <p>
	 * Title：SimpleDataSource
	 * </p>
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @version 1.0
	 * @see 
	 * @since 1.0
	 */
	public class SimpleDataSource extends DataSourceAdapter {
		
		/**
		 * DataSource Properties
		 */
		private Properties properties;
		
		/**
		 * constructor
		 * @param properties
		 */
		public SimpleDataSource(Properties properties) {
			this.properties = properties;
			
			String driverClassName = this.properties.getProperty("jdbc.driverClassName");
			try {
				Class.forName(driverClassName);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		}

		/**
		 * getConnection
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @return
		 * @throws SQLException
		 * @see javax.sql.DataSource#getConnection()
		 */
		public Connection getConnection() throws SQLException {
			String url = this.properties.getProperty("jdbc.url");
			String username = this.properties.getProperty("jdbc.username");
			String password = this.properties.getProperty("jdbc.password");
			return DriverManager.getConnection(url, username, password);
		}

		/**
		 * getConnection
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @param username
		 * @param password
		 * @return
		 * @throws SQLException
		 * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
		 */
		public Connection getConnection(String username, String password) throws SQLException {
			String url = this.properties.getProperty("jdbc.url");
			return DriverManager.getConnection(url, username, password);
		}
	}
	
	/**
	 * JndiDataSourceProxy
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @version 1.0
	 * @see 
	 * @since 1.0
	 */
	public class JndiDataSourceProxy extends DataSourceAdapter {
		
		/**
		 * DataSource Properties
		 */
		private Properties properties;
		
		/**
		 * EAP jndi 数据源名
		 */
		private DataSource jndiDataSource;
		
		/**
		 * constructor
		 * @param eapDataSourceName
		 */
		public JndiDataSourceProxy(Properties properties) {
			this.properties = properties;
			String jndiName = this.properties.getProperty("jdbc.jndiName");
			this.jndiDataSource = lookupDataSource(jndiName);
		}

		/**
		 * getConnection
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @return
		 * @throws SQLException
		 * @see javax.sql.DataSource#getConnection()
		 */
		public Connection getConnection() throws SQLException {
			return this.jndiDataSource.getConnection();
		}

		/**
		 * getConnection
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @param username
		 * @param password
		 * @return
		 * @throws SQLException
		 * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
		 */
		public Connection getConnection(String username, String password) throws SQLException {
			return this.jndiDataSource.getConnection(username, password);
		}
		
		/**
		 * 将dataSourceName转换为容器jndiName
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @param dataSourceName
		 * @return
		 */
		protected String mapJndiName(String dataSourceName) {
			String jndiName = dataSourceName;
			
			return jndiName;
		}
	}

}
